import axios from 'axios'
import { PageContext, PageType } from "../../model/PageContext.mjs";
import {getPagesUrl, getInfoUrl, getUnlistedInfoUrl} from "../../../constant/pixivUrls.js";
import {Restrict} from "../../../constant/Restrict.mjs";

export class ImageParser {

  /**
   * 页面url
   * @type {string}
   */
  url

  /**
   * 页面内容
   * @type {PageContext}
   */
  context

  /**
   * 用于取消请求
   * @type {AbortController}
   */
  abortController

  /**
   * @param {string} url
   */
  constructor(url) {
    this.url = url;
    this.context = {};
    this.abortController = new AbortController();
  }

  /**
   * 解析页面内容
   * @param login {() => Promise<void>}
   * @param getToken {() => Promise<PixivTokens>}
   */
  async parseContext(login, getToken) {
    this.parseUrl(this.url);

    const iLLustAndManga = [PageType.ILLUST, PageType.MANGA]
    if (this.context.unlistedId) {
      await this.parseUnlistedInfo();

      if (iLLustAndManga.includes(this.context.raw.illustType)) {
        await this.parseUnlistedPages();
      } else {
        throw new Error(`Invalid unlisted illust type ${this.context.raw.illustType}`);
      }
    } else {
      await this.parseInfo(this.context.id);
      // 限制级作品请求登录
      let token
      if (Restrict.RESTRICTED === this.context.raw.xRestrict) {
        token = await getToken()
      }

      if (iLLustAndManga.includes(this.context.raw.illustType)) {
        await this.parsePages(this.context.raw.illustId, token);
      } else if (this.context.raw.illustType === PageType.UGOIRA) {
        await this.parseUgoiraResource(this.context.raw.illustId);
      } else {
        throw new Error(`Invalid illust type ${this.context.raw.illustType}`);
      }
    }
  }

  /**
   * 解析url
   * @param {string} url
   */
  parseUrl(url) {
    const unlistedPattern = /artworks\/(unlisted)\/([a-z\d]+)/i;
    const unlistedMatches = url.match(unlistedPattern);

    if (unlistedMatches) {
      this.context.unlistedId = unlistedMatches[2]
      return;
    }

    let patterns = [/illust_id=(\d+)/i, /artworks\/(\d+)/i];

    for (let pattern of patterns) {
      let matches = url.match(pattern);

      if (matches) {
        this.context.id = matches[1];
        return;
      }
    }

    throw new Error(`Can't parse the illust id out. url: ${this.url}`);
  }

  /**
   * 解析插画id
   * @param url
   * @return 插画id
   */
  static parseIllustId(url) {
    const unlistedPattern = /artworks\/(unlisted)\/([a-z\d]+)/i
    const unlistedMatches = url.match(unlistedPattern)

    if (unlistedMatches) {
      return unlistedMatches[2]
    }

    let patterns = [/illust_id=(\d+)/i, /artworks\/(\d+)/i]

    for (let pattern of patterns) {
      let matches = url.match(pattern)

      if (matches) {
        return matches[1]
      }
    }

    throw new Error(`Can't parse the illust id out. url: ${this.url}`);
  }

  /**
   * 从页面源数据中解析出页面内容
   * @param {RawPageContext} context
   * @returns {Object}
   */
  parseContextSource(context) {
    const pageContext = new PageContext();
    pageContext.raw = context

    return pageContext
  }

  /**
   * 解析多图页面中被折叠的资源信息
   * @return {Promise<void>}
   */
  parseUnlistedInfo() {
    return new Promise((resolve, reject) => {
      axios.get(getUnlistedInfoUrl(this.context.unlistedId), { signal: this.abortController.signal }).then((response) => {
        if (response && response.data && response.data.body) {
          this.context = this.parseContextSource(response.data.body);

          resolve();
        } else {
          reject(new Error("Can't parse Info from pages，the response data is undefined"));
        }
      }).catch((error) => {
        reject(error);
      })
    });
  }

  /**
   * 解析单图的资源信息
   * @param {string} id
   * @returns {Promise<void>}
   */
  parseInfo(id) {
    return new Promise((resolve, reject) => {
      axios.get(getInfoUrl(id), { signal: this.abortController.signal }).then((response) => {
        if (response && response.data && response.data.body) {
          this.context = this.parseContextSource(response.data.body);

          resolve();
        } else {
          reject(new Error("Can't parse Info from pages，the response data is undefined"));
        }
      }).catch((error) => {
        reject(error);
      })
    });
  }

  /**
   * 解析未列出页面的资源
   */
  parseUnlistedPages() {
    this.context.resUrls = [this.context.raw.urls.original];
    this.context.totalResCount = 1;
  }

  /**
   * 解析页面的资源
   * @param {string} id
   * @param {PixivTokens} token
   * @returns {Promise.<any,Error>}
   */
  parsePages(id, token) {
    return new Promise((resolve, reject) => {
      axios.get(getPagesUrl(id), { headers: { authorization: token.accessToken }, signal: this.abortController.signal }).then((response) => {
        if (response && response.data && response.data.body) {
          this.context.resUrls = response.data.body.map(page => page.urls.original);
          this.context.totalResCount = this.context.resUrls.length;

          resolve();
        } else {
          reject(new Error("Can't parse Resource from pages，the response data is undefined"));
        }
      }).catch((error) => {
        reject(error);
      })
    });
  }

  /**
   * 解析动图资源
   * @param {string} id
   * @returns {Promise.<any,Error>}
   */
  parseUgoiraResource(id) {
    return new Promise((resolve, reject) => {
      axios.get(this.buildPagesUrl(id), { signal: this.abortController.signal }).then((response) => {
        if (response.error) {
          reject(new Error(response.error));
          return;
        }

        // TODO 动图源数据和其他的不一样
        this.context.illustSrc = response.data.body.src;
        this.context.illustOriginalSrc = response.data.body.originalSrc;
        this.context.illustFrames = response.data.body.frames;
        this.context.illustMimeType = response.data.body.mime_type;
        this.context.__metadata = response.data.body;

        let duration = 0

        this.context.illustFrames.forEach(function (frame) {
          duration += frame.delay;
        });

        this.context.illustDuration = duration;

        resolve();
      }).catch((error) => {
        reject(error);
      })
    })
  }

  /**
   * 终止请求
   */
  abort() {
    if (this.request) {
      this.abortController.abort();
    }
  }
}
